// Copyright (c) Microsoft Corporation. All rights reserved.

using System;
using System.Collections.Specialized;
using System.Diagnostics;

namespace Microsoft.Tools.CodeRunner
{
    /// <summary>
    /// Groups a set of useful <see cref="string" /> manipulation and validation 
    /// methods.
    /// </summary>
    public sealed class StringUtility 
    {
        #region Instance Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="StringUtility" /> class.
        /// </summary>
        /// <remarks>
        /// Prevents instantiation of the <see cref="StringUtility" /> class.
        /// </remarks>
        private StringUtility() 
        {
        }

        #endregion Private Instance Constructors

        #region Class Methods

        /// <summary>
        /// Converts an empty string ("") to <see langword="null" />.
        /// </summary>
        /// <param name="value">The value to convert.</param>
        /// <returns>
        /// <see langword="null" /> if <paramref name="value" /> is an empty 
        /// string ("") or <see langword="null" />; otherwise, <paramref name="value" />.
        /// </returns>
        public static string ConvertEmptyToNull(string value) 
        {
            if (!String.IsNullOrEmpty(value)) 
            {
                return value;
            }

            return null;
        }

        /// <summary>
        /// Converts <see langword="null" /> to an empty string.
        /// </summary>
        /// <param name="value">The value to convert.</param>
        /// <returns>
        /// An empty string if <paramref name="value" /> is <see langword="null" />;
        /// otherwise, <paramref name="value" />.
        /// </returns>
        public static string ConvertNullToEmpty(string value) 
        {
            if (value == null) 
            {
                return string.Empty;
            }

            return value;
        }

        /// <summary>
        /// Concatenates a specified separator <see cref="string" /> between each 
        /// element of a specified <see cref="StringCollection" />, yielding a 
        /// single concatenated string.
        /// </summary>
        /// <param name="separator">A <see cref="string" />.</param>
        /// <param name="value">A <see cref="StringCollection" />.</param>
        /// <returns>
        /// A <see cref="string" /> consisting of the elements of <paramref name="value" /> 
        /// interspersed with the separator string.
        /// </returns>
        /// <remarks>
        /// <para>
        /// For example if <paramref name="separator" /> is ", " and the elements 
        /// of <paramref name="value" /> are "apple", "orange", "grape", and "pear", 
        /// <see cref="Join(string, StringCollection)" /> returns "apple, orange, 
        /// grape, pear".
        /// </para>
        /// <para>
        /// If <paramref name="separator" /> is <see langword="null" />, an empty 
        /// string (<see cref="string.Empty" />) is used instead.
        /// </para>
        /// </remarks>
        public static string Join(string separator, StringCollection value) 
        {
            if (value == null) 
            {
                throw new ArgumentNullException("value");
            }

            if (separator == null) 
            {
                separator = string.Empty;
            }

            // create with size equal to number of elements in collection
            string[] elements = new String[value.Count];

            // copy elements in collection to array       
            value.CopyTo(elements, 0);

            // concatenate specified separator between each elements 
            return String.Join(separator, elements);
        }

		/// <summary>
		/// Breaks a string on word boundaries, to the given <paramref name="lineLength"/>.  
		/// Only <see cref="Char.IsWhiteSpace(char)"/> characters
		/// are considered when breaking up sentences.  Leading spaces on resulting lines are removed.
		/// </summary>
		/// <param name="text">The text to break up.</param>
		/// <param name="lineLength">The maximum length of a line</param>
		/// <returns>An array of strings no longer than the given line length</returns>
		public static string[] WordBreak(string text, int lineLength)
		{
			StringCollection lines = new StringCollection();
			
			int b = 0; // current working base
			
			while (b < text.Length)
			{
				// Move forward to avoid any spaces at the beginning of the string
				while (Char.IsWhiteSpace(text[b]) && b < text.Length)
					b++;

				int r = Math.Min(text.Length - b, lineLength);
				int e = b + r - 1;
				
				// If we did not hit the exact end of a word, move backwards to the beginning of the word
				if (e < text.Length - 1 && !Char.IsWhiteSpace(text[e + 1]))
				{
					while (!Char.IsWhiteSpace(text[e]) && e > b)
						e--;
				}
				
				// We couldn't find any whitespace, so the use the whole word chunk 	
				if (e == b)
					e = b + r - 1;
				
				Debug.Assert(e - b + 1 <= lineLength);
					
				lines.Add(text.Substring(b, e - b + 1));
				
				b = e + 1;
			}
			
			string[] ret = new string[lines.Count];
			
			lines.CopyTo(ret, 0);
			
			return ret; 
		}

        #endregion Public Static Methods
    }
}
